//-----------------------------------------------------------------------
// <copyright file="CheckHeartBeat.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Mark Phillips</author>
// <email>v-mphill@microsoft.com</email>
// <date>2004-05-24</date>
// <summary>Checks if the Virtual Machine's Guest OS has started.</summary>
//-----------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks.VirtualServer.VirtualMachine.OS
{
    #region Using directives

    using System;
    using System.Reflection;
    using System.Collections;
    using Microsoft.Build.Framework;
    using System.Runtime.InteropServices;
    //using Microsoft.VirtualServer.Interop;

    #endregion

    /// <summary>
    /// Checks if the Virtual Machine's Guest OS has started.
    /// </summary>          
    /// <remarks>
    /// <code><![CDATA[
    /// <VirtualServer.VirtualMachine.OS.CheckHeartBeat
    ///             MachineName="machineName"
    ///             ServerName="serverName"
    ///             Timeout="timeout"
    ///             Retries="retries" >
    ///             <Output TaskParameter="IsAlive" ItemName="aliveItem"></Output>
    /// </VirtualServer.VirtualMachine.OS.CheckHeartBeat>
    /// ]]></code>
    /// <para>where:</para>
    /// <para><i>machineName (Required)</i></para>
    /// <para>The name of the virtual machine to check.</para>
    /// <para><i>serverName</i></para>
    /// <para>Set the <i>serverName</i> property to specify a remote server to connect to. Defaults to connect locally.</para>
    /// <para><i>timeout</i></para>
    /// <para>Length of time to wait for the task to wait for a response, in milliseconds. Defaults to 10.</para>
    /// <para><i>aliveItem</i></para>
    /// <para>Returns true if the task receives a response from the virtual server within the specified <i>timeout</i> period and within
    /// the number of retries specified by <i>retries</i>. Otherwise, returns false.</para>
    /// </remarks>
    /// <example>
    /// <code><![CDATA[
    /// <Project>
    ///     <Target Name="Test" >
    ///         <VirtualServer.VirtualMachine.OS.CheckHeartBeat
    ///             MachineName="myMachine"
    ///             Timeout="20"
    ///             <Output TaskParameter="IsAlive" ItemName="AliveItem"></Output>
    ///         </VirtualServer.VirtualMachine.OS.CheckHeartBeat> 
    ///     </Target>
    /// </Project>
    /// ]]></code>    
    /// </example>
    public class CheckHeartBeat : VirtualServerBase
    {
        private string serverName;
        private string machineName;
        private int timeout = 10;
        private int retries = 1;
        private bool alive = false;      
        private const uint VM_E_ADDITIONS_NOT_AVAIL = 0xa0040504;

        /// <summary>
        /// Initializes a new instance of the CheckHeartBeat class.
        /// </summary>
        public CheckHeartBeat()
        {      
        }

        
        /// <summary>
        /// Performs the action of this task.
        /// </summary>
        protected override void InternalExecute()
        {
            if (this.machineName == null || this.machineName.Length == 0)
            {
                throw new TaskException("VirtualServer.InvalidMachineName", null);
            }

            //reset output values incase someone calls execute more than once on the same object
            IsAlive = false;

            ComWrapper virtualMachines = GetVirtualMachinesFromServer(this.serverName);
            ComWrapper virtualMachine = GetVirtualMachine(virtualMachines, this.machineName);

            NativeMethods.VMState state = (NativeMethods.VMState)virtualMachine.GetProperty("State");

            if (state == NativeMethods.VMState.VMState_TurnedOff)
            {
                throw new TaskException("VirtualServer.MachineNotRunning", MachineName);
            }

            ComWrapper guestOS = new ComWrapper(virtualMachine.GetProperty("GuestOS"), true);

            int retryCount = 0;

            do
            {
                Log.LogMessage(MessageImportance.Low, "Checking for OS heart beat.");

                try
                {
                    alive = (bool)guestOS.GetProperty("IsHeartbeating");
                }
                catch (Exception ex)
                {
                    if (ex.InnerException is COMException)
                    {
                        COMException cex = (COMException)ex.InnerException;

                        if (((uint)cex.ErrorCode) == VM_E_ADDITIONS_NOT_AVAIL)
                        {
                            Log.LogMessage(MessageImportance.Low, "Additions not currently available on Guest OS.");
                        }
                        else
                        {
                            Log.LogMessage(MessageImportance.High, String.Format("An error occurred calling IsHeartBeating on the GuestOS. COM Error: {0}.", cex.Message));
                            throw ex;
                        }
                    }
                    else
                    {
                        Log.LogMessage(MessageImportance.High, String.Format("An exception occurred that could not be handled.  Exception details: {0}.", ex.Message));
                        throw ex;
                    }
                }

                if (alive) { return; }

                retryCount++;
                System.Threading.Thread.Sleep(timeout * 1000);
            }
            while (retryCount < this.retries);
        }

        /// <summary>
        /// Returns true if the task receives a response from the virtual server within the specified <i>timeout</i> period and within
        /// the number of retries specified by <i>retries</i>. Otherwise, returns false.
        /// </summary>
        /// <value>Returns true if the task receives a response from the virtual server within the specified <i>timeout</i> period and within
        /// the number of retries specified by <i>retries</i>. Otherwise, returns false.</value>
        [Output]
        public bool IsAlive
        {
            get
            {
                return alive;
            }
            set
            {
                alive = value;
            }
        }


        /// <summary>
        /// The number of subsequent attempts the task should make to obtain a response from the virtual machine, if no response was obtained within the time specified by the <i>timeout</i>
        /// property.
        /// </summary>
        /// <value>The number of subsequent attempts the task should make to obtain a response from the virtual machine, if no response was obtained within the time specified by the <i>timeout</i>
        /// property. Defaults to 1.</value>
        public int Retries
        {
            get
            {
                return retries;
            }
            set
            {
                retries = value;
            }
        }

        /// <summary>
        /// Length of time to wait for the a response from the virtual machine, in seconds.
        /// </summary>
        /// <value>Length of time to wait for the a response from the virtual machine, in seconds. Defaults to 10.</value>
        public int Timeout
        {
            get
            {
                return timeout;
            }
            set
            {
                timeout = value;
            }
        }

        /// <summary>
        /// Set the <i>serverName</i> property to specify a remote server to connect to.
        /// </summary>
        /// <value>Set the <i>serverName</i> property to specify a remote server to connect to. Defaults to connect locally.</value>
        public string ServerName
        {
            get
            {
                return (serverName == null ? String.Empty : serverName);
            }
            set
            {
                serverName = value;
            }
        }

        /// <summary>
        /// The name of the virtual machine to check.
        /// </summary>
        /// <value>The name of the virtual machine to check.</value>
        [Required]
        public string MachineName
        {
            get
            {
                return (machineName == null ? String.Empty : machineName);
            }
            set
            {
                machineName = value;
            }
        }

    }
}